using System;
using MvpFramework;
using MvpFramework.Specifications;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;
using TestNameSpace.View;

namespace MvpFramework.UnitTests
{
    [TestFixture]
    public class PresenterTestFixture
    {
        private MockRepository _mocker;
        private ISomethingThatHasAFailedSpecView _badView;
        private ISomethingThatHasAPassedSpecView _goodView;

        private void Arrange()
        {
            _mocker = new MockRepository();
            _badView = _mocker.DynamicMock<ISomethingThatHasAFailedSpecView>();
            _goodView = _mocker.DynamicMock<ISomethingThatHasAPassedSpecView>();
        }

        private void Act()
        {
            _mocker.ReplayAll();
        }

        public class PresenterThatFails : Presenter<ISomethingThatHasAFailedSpecView>
        {
            private PresenterValidatorThatFails v;

            public PresenterThatFails(ISomethingThatHasAFailedSpecView view)
                : base(view)
            {
            }

            protected override PresenterValidator<ISomethingThatHasAFailedSpecView> PresenterValidator
            {
                get
                {
                    if (v == null)
                    {
                        v = new PresenterValidatorThatFails(this);
                    }
                    return v;
                }
            }
        }

        public class PresenterValidatorThatFails : PresenterValidator<ISomethingThatHasAFailedSpecView>
        {
            public PresenterValidatorThatFails(PresenterThatFails p)
                : base(p)
            {
            }

            protected internal override Func<bool> ViewIsValidFunction
            {
                get
                {
                    return () => new MySpec("Blah")
                                     .Or(
                                     new MySpec("Blah2")
                                     )
                                     .Not(
                                     new MySpec("Blah3")
                                     ).IsSatisfiedBy("FailMe");
                }
            }
        }

        public class NonDefaultPresenterThatPasses : Presenter<ISomethingThatHasAFailedSpecView>
        {
            private PresenterValidator<ISomethingThatHasAFailedSpecView> v;

            public NonDefaultPresenterThatPasses(ISomethingThatHasAFailedSpecView view)
                : base(view)
            {
            }

            protected override PresenterValidator<ISomethingThatHasAFailedSpecView> PresenterValidator
            {
                get
                {
                    if (v == null)
                        v = new PassingPresenterValidator(this);
                    return v;
                }
            }
        }

        public class PassingPresenterValidator : PresenterValidator<ISomethingThatHasAFailedSpecView>
        {
            public PassingPresenterValidator(Presenter<ISomethingThatHasAFailedSpecView> presenter)
                : base(presenter)
            {
            }

            protected internal override Func<bool> ViewIsValidFunction
            {
                get
                {
                    return () => new MySpec("Blah")
                                     .Or(
                                     new MySpec("Blah2")
                                     )
                                     .Not(
                                     new MySpec("Blah3")
                                     ).IsSatisfiedBy("Blah2");
                }
            }
        }

        public class MySpec : Specification<string>
        {
            private readonly string _mustMatch;

            public MySpec(string mustMatch)
            {
                _mustMatch = mustMatch;
            }

            public override bool IsSatisfiedBy(string obj)
            {
                return obj == _mustMatch;
            }
        }

        [Test]
        public void constructor_sets_View()
        {
            Arrange();
            Act();
            var presenter = _mocker.PartialMock<Presenter<ISomethingThatHasAPassedSpecView>>(_goodView);

            Assert.That(presenter.View, Is.EqualTo(_goodView));
        }

        [Test]
        public void overriding_Validate_works_as_expected()
        {
            Arrange();
            Act();
            var presenter = new NonDefaultPresenterThatPasses(_badView);
            Assert.That(presenter.IsValid, Is.True);
            var badPresenter = new PresenterThatFails(_badView);
            Assert.That(badPresenter.IsValid, Is.False);
        }

        [Test]
        public void Validate_Checks_GetMessage_for_error_message()
        {
            Arrange();
            var presenter = _mocker.PartialMock<Presenter<ISomethingThatHasAFailedSpecView>>(_badView);
            // Thanks Matt!
            // Bug in Rhino mocks, must call record or PartialMock expects that the whole object is mocked.
            using (_mocker.Record())
            {
            }
            Act();

            _mocker.Verify(_badView);
        }

        [Test]
        public void Validate_returns_false_for_invalid_view()
        {
            Arrange();
            Act();
            var presenter = _mocker.PartialMock<Presenter<ISomethingThatHasAFailedSpecView>>(_badView);
            _mocker.Replay(presenter);

            Assert.That(presenter.IsValid, Is.False);
        }
    }
}